package jp.dodododo.aop.interceptors;

import java.lang.reflect.Modifier;

import jp.dodododo.aop.annotation.Enhance;

import org.aopalliance.intercept.ConstructorInvocation;
import org.aopalliance.intercept.Invocation;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * {@.en }
 * 
 * <br />
 * 
 * {@.ja トレース出力用のInterceptorです。}
 * 
 * @author Satoshi Kimura
 */
@Enhance(false)
public class TraceInterceptor extends BothInterceptor {

	private static final Log logger = LogFactory.getLog(TraceInterceptor.class);

	private static final TraceInterceptor INSTANCE = new TraceInterceptor();

	public static TraceInterceptor getInstance() {
		return INSTANCE;
	}

	@Override
	protected Object process(final Invocation invocation) throws Throwable {
		if (getLogger(invocation).isDebugEnabled() == false) {
			return invocation.proceed();
		}
		final StringBuffer buf = new StringBuffer(100);
		buf.append(getTargetClass(invocation).getName());
		buf.append("#");
		if (invocation instanceof MethodInvocation) {
			MethodInvocation methodInvocation = (MethodInvocation) invocation;
			buf.append(methodInvocation.getMethod().getName());
		} else if (invocation instanceof ConstructorInvocation) {
			int modifiers = ((ConstructorInvocation) invocation).getConstructor().getModifiers();
			if (Modifier.isStatic(modifiers) == true) {
				buf.append("<clinit>");
			} else {
				buf.append("<init>");
			}
		}
		appendArgsInfo(buf, invocation);
		Object ret = null;
		Throwable cause = null;
		getLogger(invocation).debug("BEGIN " + buf);
		try {
			ret = invocation.proceed();
			appendReturnInfo(buf, ret);
		} catch (final Throwable t) {
			buf.append(" Throwable:").append(t);
			cause = t;
		}
		if (cause == null) {
			getLogger(invocation).debug("END " + buf);
			return ret;
		} else {
			getLogger(invocation).debug("END " + buf, cause);
			throw cause;
		}
	}

	protected void appendReturnInfo(final StringBuffer buf, Object ret) {
		buf.append(" : ");
		appendObject(buf, ret);
	}

	protected void appendArgsInfo(final StringBuffer buf, final Invocation invocation) {
		buf.append("(");
		final Object[] args = invocation.getArguments();
		if (args != null && args.length > 0) {
			for (int i = 0; i < args.length; ++i) {
				appendObject(buf, args[i]).append(", ");
			}
			buf.setLength(buf.length() - 2);
		}
		buf.append(")");
	}

	private StringBuffer appendObject(final StringBuffer buf, final Object o) {
		ToStringer.append(buf, o);
		return buf;
	}

	protected Log getLogger(Invocation invocation) {
		return logger;
	}

}
